#include "Zombie.h"
#include "ZombieSwarm.h"

Zombie::Zombie(btDiscreteDynamicsWorld* pWorld,
               btCylinderShape* pZombieCollisionShape,
               Ogre::SceneManager* pSceneManager,
               OpenSteer::Vec3 position,
               Human* pHuman,
               ZombieSwarm* pSwarm) :
  Character::Character(pWorld, pZombieCollisionShape, pSceneManager,
                       position, ZOMBIEMESH),
  _pHuman(pHuman), _pSwarm(pSwarm) {

  // AnimationState
  Ogre::AnimationState* zombieAnimation =
    _pEntity->getAnimationState("Walk");
  zombieAnimation->setEnabled(true);
  zombieAnimation->setLoop(true);
  zombieAnimation->setTimePosition(0.0);

  // OpenSter ----------------------------------------------------------

  // Bullet vehicle
  setMaxForce(10);
  setMaxSpeed(2);
}

void
Zombie::update(const float currentTime, const float elapsedTime) {

  // Orientar el nodo del zombi respecto de su movimiento.
  // Esto no debería hacerse aquí, pero por ahora es la única solución
  // que se me ha ocurrido.

  Ogre::Vector3 ogreDirection(forward().x, forward().y, forward().z);
  _pNode->setDirection(-ogreDirection);

  // Animar el zombi
  _pEntity->getAnimationState("Walk")->addTime(elapsedTime);

  // Aplicar fuerzas de comportamientos
  applySteeringForce(steerForZombie(elapsedTime));
}

/*
  Esta función determina el comportamiento de los zombies. En esta versión
  es el siguiente:

  1: Avanzar.
  2: Vagar.
  3: Perseguir al humano
  4: "Llamada zombi" si el humano se acerca a la horda
  5: Ir al centro de la horda si se superó la distancia máxima.

 */
OpenSteer::Vec3
Zombie::steerForZombie(const float elapsedTime) {

  OpenSteer::Vec3 zombieForce(0, 0, 0);

  // Avanzar -----------------------------------------------------------

  // Si la velocidad es poca, avanzar (XXX ¿Añadir _minSpeed?)
  if (speed() < 1) zombieForce += forward();

  // Vagar -------------------------------------------------------------

  zombieForce += steerForWander(elapsedTime);

  // Perseguir al humano -----------------------------------------------

  float distanceToHuman =
    OpenSteer::Vec3::distance(position(), _pHuman->position());

  if (distanceToHuman < MINDISTANCETOHUMAN) {

    zombieForce += steerForSeek(_pHuman->position());
    return zombieForce.setYtoZero();
  }

  // Llamada zombi -----------------------------------------------------

  // Si el zombi no pertenece a ninguna orda, se ignora el comportamiento
  if (_pSwarm == NULL) return zombieForce.setYtoZero();

  float swarmDistanceToHuman =
    OpenSteer::Vec3::distance(_pSwarm->swarmCenter(),
                              _pHuman->position());

  if (swarmDistanceToHuman <
      _pSwarm->maxDistance() + MINDISTANCETOHUMAN) {

    zombieForce += steerForSeek(_pHuman->position());
    return zombieForce.setYtoZero();
  }

  // Ir al centro de la horda ------------------------------------------

  float distanceToSwarm =
    OpenSteer::Vec3::distance(position(), _pSwarm->swarmCenter());

  if (distanceToSwarm > _pSwarm->maxDistance())
    zombieForce += steerForSeek(_pSwarm->swarmCenter());

  // Devolver fuerza eliminando la componente Y ------------------------

  return zombieForce.setYtoZero();
}
